/*
 *  Copyright 2009 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.mybatis.generator.codegen.mybatis3.model;

import static org.mybatis.generator.internal.util.JavaBeansUtil.getGetterMethodName;
import static org.mybatis.generator.internal.util.messages.Messages.getString;

import java.util.ArrayList;
import java.util.List;

import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.FullyQualifiedTable;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.Plugin;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.codegen.AbstractJavaGenerator;
import org.mybatis.generator.codegen.RootClassInfo;

/**
 * 
 * @author Jeff Butler
 * 
 */
public class BusinessRecordGenerator extends AbstractJavaGenerator {

	private FullyQualifiedJavaType jsonSerialize;
	private FullyQualifiedJavaType poType;

	public BusinessRecordGenerator() {
		super();
		jsonSerialize = new FullyQualifiedJavaType(
				"com.fasterxml.jackson.databind.annotation.JsonSerialize");
	}

	@Override
	public List<CompilationUnit> getCompilationUnits() {
		FullyQualifiedTable table = introspectedTable.getFullyQualifiedTable();
		progressCallback.startTask(getString("Progress.8", table.toString())); //$NON-NLS-1$
		Plugin plugins = context.getPlugins();
		CommentGenerator commentGenerator = context.getCommentGenerator();

		String poTypePackage = introspectedTable.getBaseRecordType();

		String boTypePackage = introspectedTable.getBaseRecordType();
		if (boTypePackage != null && boTypePackage.lastIndexOf(".po.") != -1) {
			boTypePackage = boTypePackage.replace(".po.", ".bo.");
		}
		// boTypePackage += "Bo";
		boTypePackage = util.StringUtils.replaceLast(boTypePackage,"Po","Bo");
		poType = new FullyQualifiedJavaType(poTypePackage);
		FullyQualifiedJavaType type = new FullyQualifiedJavaType(boTypePackage);
		TopLevelClass topLevelClass = new TopLevelClass(type);
		topLevelClass.setVisibility(JavaVisibility.PUBLIC);

		topLevelClass.addImportedType(poTypePackage);
		topLevelClass.addImportedType(jsonSerialize);
		topLevelClass
				.addAnnotation("@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)");

		int index = boTypePackage.lastIndexOf("bo");
		boTypePackage = boTypePackage.substring(0, index + 3);

		FullyQualifiedJavaType superClass = new FullyQualifiedJavaType(
				boTypePackage + "BaseBo");

		topLevelClass.setSuperClass(superClass);
		topLevelClass.addImportedType(superClass);

		commentGenerator.addJavaFileComment(topLevelClass);
		commentGenerator.addClassComment(topLevelClass, introspectedTable,
				true, introspectedTable.getRemarks());

		List<IntrospectedColumn> introspectedColumns = getColumnsInThisClass();

		if (introspectedTable.isConstructorBased()) {
			addParameterizedConstructor(topLevelClass);

			if (!introspectedTable.isImmutable()) {
				addDefaultConstructor(topLevelClass);
			}
		}
		addInitConstructor(topLevelClass);
		addConstructor(topLevelClass);

		String rootClass = getRootClass();
		for (IntrospectedColumn introspectedColumn : introspectedColumns) {
			if (RootClassInfo.getInstance(rootClass, warnings)
					.containsProperty(introspectedColumn)) {
				continue;
			}

			Field field = getJavaBeansField(introspectedColumn);
			if (plugins.modelFieldGenerated(field, topLevelClass,
					introspectedColumn, introspectedTable,
					Plugin.ModelClassType.BASE_RECORD)) {
				topLevelClass.addField(field);
				topLevelClass.addImportedType(field.getType());
			}

			Method method = getJavaBeansGetter(introspectedColumn);
			if (plugins.modelGetterMethodGenerated(method, topLevelClass,
					introspectedColumn, introspectedTable,
					Plugin.ModelClassType.BASE_RECORD)) {
				topLevelClass.addMethod(method);
			}

			if (!introspectedTable.isImmutable()) {
				method = getJavaBeansSetter(introspectedColumn);
				if (plugins.modelSetterMethodGenerated(method, topLevelClass,
						introspectedColumn, introspectedTable,
						Plugin.ModelClassType.BASE_RECORD)) {
					topLevelClass.addMethod(method);
				}
			}
			/**
			 * if (field.getName().length() > 2 &&
			 * field.getName().lastIndexOf("Id") != -1) { Field temp=new
			 * Field(); String name=field.getName(); name=name.replace("Id",
			 * "")+"Name"; FullyQualifiedJavaType
			 * fqjt=FullyQualifiedJavaType.getStringInstance();
			 * temp.setName(name); temp.setType(fqjt);
			 * temp.setVisibility(field.getVisibility());
			 * 
			 * if (plugins.modelFieldGenerated(temp, topLevelClass,
			 * introspectedColumn, introspectedTable,
			 * Plugin.ModelClassType.BASE_RECORD)) {
			 * topLevelClass.addField(temp);
			 * topLevelClass.addImportedType(temp.getType()); }
			 * 
			 * method = getJavaBeansGetter(introspectedColumn); if
			 * (plugins.modelGetterMethodGenerated(method, topLevelClass,
			 * introspectedColumn, introspectedTable,
			 * Plugin.ModelClassType.BASE_RECORD)) {
			 * topLevelClass.addMethod(method); }
			 * 
			 * if (!introspectedTable.isImmutable()) { method =
			 * getJavaBeansSetter(introspectedColumn); if
			 * (plugins.modelSetterMethodGenerated(method, topLevelClass,
			 * introspectedColumn, introspectedTable,
			 * Plugin.ModelClassType.BASE_RECORD)) {
			 * topLevelClass.addMethod(method); } } }
			 */
		}
		// addToStringMethod(topLevelClass, introspectedTable);
		List<CompilationUnit> answer = new ArrayList<CompilationUnit>();
		if (context.getPlugins().modelBaseRecordClassGenerated(topLevelClass,
				introspectedTable)) {
			answer.add(topLevelClass);
		}
		return answer;
	}

	/**
	 * 添加toString方法
	 * 
	 * @param topLevelClass
	 */
	protected void addToStringMethod(TopLevelClass topLevelClass,
			IntrospectedTable introspectedTable) {
		String tableName = introspectedTable.getTableConfiguration()
				.getTableName();
		List<IntrospectedColumn> introspectedColumns = getColumnsInThisClass();

		Method method = new Method();
		method.setName("toString");
		method.setReturnType(new FullyQualifiedJavaType("String"));
		method.setVisibility(JavaVisibility.PUBLIC);
		StringBuilder sb = new StringBuilder();
		sb.append("return \"" + tableName + "{\"");
		int i = 0;
		for (IntrospectedColumn introspectedColumn : introspectedColumns) {
			String property = introspectedColumn.getJavaProperty();
			if (i == 0) {
				sb.append("+\"" + property + "["
						+ introspectedColumn.getRemarks() + "]=\"+" + property);
			} else {
				sb.append("+\"," + property + "["
						+ introspectedColumn.getRemarks() + "]=\"+" + property);
			}
			++i;
		}
		sb.append("+\"}\";");
		method.addBodyLine(sb.toString());
		method.addAnnotation("@Override");
		topLevelClass.addMethod(method);
	}

	private FullyQualifiedJavaType getSuperClass() {
		FullyQualifiedJavaType superClass;
		if (introspectedTable.getRules().generatePrimaryKeyClass()) {
			superClass = new FullyQualifiedJavaType(
					introspectedTable.getPrimaryKeyType());
		} else {
			String rootClass = getRootClass();
			if (rootClass != null) {
				superClass = new FullyQualifiedJavaType(rootClass);
			} else {
				superClass = null;
			}
		}

		return superClass;
	}

	private boolean includePrimaryKeyColumns() {
		return !introspectedTable.getRules().generatePrimaryKeyClass()
				&& introspectedTable.hasPrimaryKeyColumns();
	}

	private boolean includeBLOBColumns() {
		return !introspectedTable.getRules().generateRecordWithBLOBsClass()
				&& introspectedTable.hasBLOBColumns();
	}

	private void addParameterizedConstructor(TopLevelClass topLevelClass) {
		Method method = new Method();
		method.setVisibility(JavaVisibility.PUBLIC);
		method.setConstructor(true);
		method.setName(topLevelClass.getType().getShortName());
		context.getCommentGenerator().addGeneralMethodComment(method,
				introspectedTable);

		List<IntrospectedColumn> constructorColumns = includeBLOBColumns() ? introspectedTable
				.getAllColumns() : introspectedTable.getNonBLOBColumns();

		for (IntrospectedColumn introspectedColumn : constructorColumns) {
			method.addParameter(new Parameter(introspectedColumn
					.getFullyQualifiedJavaType(), introspectedColumn
					.getJavaProperty()));
		}

		StringBuilder sb = new StringBuilder();
		if (introspectedTable.getRules().generatePrimaryKeyClass()) {
			boolean comma = false;
			sb.append("super("); //$NON-NLS-1$
			for (IntrospectedColumn introspectedColumn : introspectedTable
					.getPrimaryKeyColumns()) {
				if (comma) {
					sb.append(", "); //$NON-NLS-1$
				} else {
					comma = true;
				}
				sb.append(introspectedColumn.getJavaProperty());
			}
			sb.append(");"); //$NON-NLS-1$
			method.addBodyLine(sb.toString());
		}

		List<IntrospectedColumn> introspectedColumns = getColumnsInThisClass();

		for (IntrospectedColumn introspectedColumn : introspectedColumns) {
			sb.setLength(0);
			sb.append("this."); //$NON-NLS-1$
			sb.append(introspectedColumn.getJavaProperty());
			sb.append(" = "); //$NON-NLS-1$
			sb.append(introspectedColumn.getJavaProperty());
			sb.append(';');
			method.addBodyLine(sb.toString());
		}

		topLevelClass.addMethod(method);
	}

	private void addInitConstructor(TopLevelClass topLevelClass) {
		Method method = new Method();
		method.setName(topLevelClass.getType().getShortName());
		method.setReturnType(new FullyQualifiedJavaType(""));
		method.setVisibility(JavaVisibility.PUBLIC);
		StringBuilder sb = new StringBuilder();
		method.addBodyLine(sb.toString());
		topLevelClass.addMethod(method);
	}

	private void addConstructor(TopLevelClass topLevelClass) {
		Method method = new Method();
		method.setVisibility(JavaVisibility.PUBLIC);
		method.setConstructor(true);
		method.setName(topLevelClass.getType().getShortName());
		context.getCommentGenerator().addGeneralMethodComment(method,
				introspectedTable);

		method.addParameter(new Parameter(poType, "po"));

		StringBuilder sb = new StringBuilder();
		if (introspectedTable.getRules().generatePrimaryKeyClass()) {
			boolean comma = false;
			sb.append("super("); //$NON-NLS-1$
			for (IntrospectedColumn introspectedColumn : introspectedTable
					.getPrimaryKeyColumns()) {
				if (comma) {
					sb.append(", "); //$NON-NLS-1$
				} else {
					comma = true;
				}
				sb.append(introspectedColumn.getJavaProperty());
			}
			sb.append(");"); //$NON-NLS-1$
			method.addBodyLine(sb.toString());
		}

		List<IntrospectedColumn> introspectedColumns = getColumnsInThisClass();

		for (IntrospectedColumn introspectedColumn : introspectedColumns) {
			FullyQualifiedJavaType fqjt = introspectedColumn
					.getFullyQualifiedJavaType();
			String property = introspectedColumn.getJavaProperty();
			sb.setLength(0);
			sb.append("this."); //$NON-NLS-1$
			sb.append(introspectedColumn.getJavaProperty());
			sb.append(" = po."); //$NON-NLS-1$
			sb.append(getGetterMethodName(property, fqjt));
			sb.append("();");
			method.addBodyLine(sb.toString());
		}

		topLevelClass.addMethod(method);
	}

	private List<IntrospectedColumn> getColumnsInThisClass() {
		List<IntrospectedColumn> introspectedColumns;
		if (includePrimaryKeyColumns()) {
			if (includeBLOBColumns()) {
				introspectedColumns = introspectedTable.getAllColumns();
			} else {
				introspectedColumns = introspectedTable.getNonBLOBColumns();
			}
		} else {
			if (includeBLOBColumns()) {
				introspectedColumns = introspectedTable
						.getNonPrimaryKeyColumns();
			} else {
				introspectedColumns = introspectedTable.getBaseColumns();
			}
		}

		return introspectedColumns;
	}
}
